home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / screen.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  46KB  |  1,985 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * screen.c: code for displaying on the screen
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "param.h"
  17.  
  18. char *tgoto __PARMS((char *cm, int col, int line));
  19.  
  20. static char_u     *Nextscreen = NULL;     /* What is currently on the screen. */
  21. static char_u     **LinePointers = NULL;    /* array of pointers into Nextscreen */
  22.  
  23. /*
  24.  * Cline_height is set (in cursupdate) to the number of physical
  25.  * lines taken by the line the cursor is on. We use this to avoid extra calls
  26.  * to plines(). The optimized routine updateline()
  27.  * makes sure that the size of the cursor line hasn't changed. If so, lines
  28.  * below the cursor will move up or down and we need to call the routine
  29.  * updateScreen() to examine the entire screen.
  30.  */
  31. static int        Cline_height;            /* current size of cursor line */
  32.  
  33. static int        Cline_row;                /* starting row of the cursor line on screen */
  34.  
  35. static FPOS        old_cursor = {0, 0};    /* last known end of visual part */
  36. static int        oldCurswant = 0;        /* last known value of Curswant */
  37. static int        canopt;                    /* TRUE when cursor goto can be optimized */
  38. static int        invert = 0;                /* set to INVERTCODE when inverting */
  39.  
  40. #define INVERTCODE        0x80
  41.  
  42. static int win_line __ARGS((WIN *, linenr_t, int, int));
  43. static void screen_char __ARGS((char_u *, int, int));
  44. static void screenalloc __ARGS((int));
  45. static void screenclear2 __ARGS((void));
  46. static int screen_ins_lines __ARGS((int, int, int, int));
  47.  
  48. /*
  49.  * updateline() - like updateScreen() but only for cursor line
  50.  *
  51.  * This determines whether or not we need to call updateScreen() to examine
  52.  * the entire screen for changes. This occurs if the size of the cursor line
  53.  * (in rows) hasn't changed.
  54.  */
  55.     void
  56. updateline()
  57. {
  58.     int         row;
  59.     int         n;
  60.  
  61.     if (must_redraw)        /* must redraw whole screen */
  62.     {
  63.         updateScreen(must_redraw);
  64.         return;
  65.     }
  66.  
  67.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  68.  
  69.     if (Nextscreen == NULL || RedrawingDisabled)
  70.         return;
  71.  
  72.     screen_start();            /* init cursor position of screen_char() */
  73.     cursor_off();
  74.  
  75.     (void)set_highlight('v');
  76.     row = win_line(curwin, curwin->w_cursor.lnum, Cline_row, curwin->w_height);
  77.  
  78.     if (row == curwin->w_height + 1)            /* line too long for window */
  79.         updateScreen(VALID_TO_CURSCHAR);
  80.     else
  81.     {
  82.         n = row - Cline_row;
  83.         if (n != Cline_height)        /* line changed size */
  84.         {
  85.             if (n < Cline_height)     /* got smaller: delete lines */
  86.                 win_del_lines(curwin, row, Cline_height - n, FALSE, TRUE);
  87.             else                    /* got bigger: insert lines */
  88.                 win_ins_lines(curwin, Cline_row + Cline_height, n - Cline_height, FALSE, TRUE);
  89.             updateScreen(VALID_TO_CURSCHAR);
  90.         }
  91.     }
  92. }
  93.  
  94. /*
  95.  * updateScreen()
  96.  *
  97.  * Based on the current value of curwin->w_topline, transfer a screenfull
  98.  * of stuff from Filemem to Nextscreen, and update curwin->w_botline.
  99.  */
  100.  
  101.     void
  102. updateScreen(type)
  103.     int             type;
  104. {
  105.     WIN                *wp;
  106.  
  107.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  108.     if (Nextscreen == NULL)
  109.         return;
  110.  
  111.     if (must_redraw)
  112.     {
  113.         if (type < must_redraw)        /* use maximal type */
  114.             type = must_redraw;
  115.         must_redraw = 0;
  116.     }
  117.  
  118.     if (type == CURSUPD)        /* update cursor and then redraw NOT_VALID*/
  119.     {
  120.         curwin->w_lsize_valid = 0;
  121.         cursupdate();            /* will call updateScreen() */
  122.         return;
  123.     }
  124.     if (curwin->w_lsize_valid == 0 && type != CLEAR)
  125.         type = NOT_VALID;
  126.  
  127.      if (RedrawingDisabled)
  128.     {
  129.         must_redraw = type;        /* remember type for next time */
  130.         return;
  131.     }
  132.  
  133.     /*
  134.      * if the screen was scrolled up when displaying a message, scroll it down
  135.      */
  136.     if (msg_scrolled)
  137.     {
  138.         clear_cmdline = TRUE;
  139.         if (msg_scrolled > Rows - 5)        /* clearing is faster */
  140.             type = CLEAR;
  141.         else if (type != CLEAR)
  142.         {
  143.             if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows) == FAIL)
  144.                 type = CLEAR;
  145.             win_rest_invalid(firstwin);        /* should do only first/last few */
  146.         }
  147.         msg_scrolled = 0;
  148.     }
  149.  
  150.     /*
  151.      * reset cmdline_row now (may have been changed temporarily)
  152.      */
  153.     compute_cmdrow();
  154.  
  155.     if (type == CLEAR)            /* first clear screen */
  156.     {
  157.         screenclear();            /* will reset clear_cmdline */
  158.         type = NOT_VALID;
  159.     }
  160.  
  161.     if (clear_cmdline)
  162.         gotocmdline(TRUE, NUL);    /* first clear cmdline */
  163.  
  164. /* return if there is nothing to do */
  165.     if ((type == VALID && curwin->w_topline == curwin->w_lsize_lnum[0]) ||
  166.             (type == INVERTED && old_cursor.lnum == curwin->w_cursor.lnum &&
  167.                     old_cursor.col == curwin->w_cursor.col && curwin->w_curswant == oldCurswant))
  168.         return;
  169.  
  170.     curwin->w_redr_type = type;
  171.  
  172. /*
  173.  * go from top to bottom through the windows, redrawing the ones that need it
  174.  */
  175.     cursor_off();
  176.     for (wp = firstwin; wp; wp = wp->w_next)
  177.     {
  178.         if (wp->w_redr_type)
  179.             win_update(wp);
  180.         if (wp->w_redr_status)
  181.             win_redr_status(wp);
  182.     }
  183.     if (redraw_cmdline)
  184.         showmode();
  185. }
  186.  
  187. /*
  188.  * update a single window
  189.  *
  190.  * This may cause the windows below it also to be redrawn
  191.  */
  192.     void
  193. win_update(wp)
  194.     WIN        *wp;
  195. {
  196.     int                type = wp->w_redr_type;
  197.     register int    row;
  198.     register int    endrow;
  199.     linenr_t        lnum;
  200.     linenr_t        lastline = 0; /* only valid if endrow != Rows -1 */
  201.     int                done;        /* if TRUE, we hit the end of the file */
  202.     int                didline;    /* if TRUE, we finished the last line */
  203.     int             srow = 0;    /* starting row of the current line */
  204.     int             idx;
  205.     int             i;
  206.     long             j;
  207.  
  208.     if (type == NOT_VALID)
  209.     {
  210.         wp->w_redr_status = TRUE;
  211.         wp->w_lsize_valid = 0;
  212.     }
  213.  
  214.     idx = 0;
  215.     row = 0;
  216.     lnum = wp->w_topline;
  217.  
  218.     /* The number of rows shown is w_height. */
  219.     /* The default last row is the status/command line. */
  220.     endrow = wp->w_height;
  221.  
  222.     if (type == VALID || type == VALID_TO_CURSCHAR)
  223.     {
  224.         /*
  225.          * We handle two special cases:
  226.          * 1: we are off the top of the screen by a few lines: scroll down
  227.          * 2: wp->w_topline is below wp->w_lsize_lnum[0]: may scroll up
  228.          */
  229.         if (wp->w_topline < wp->w_lsize_lnum[0])    /* may scroll down */
  230.         {
  231.             j = wp->w_lsize_lnum[0] - wp->w_topline;
  232.             if (j < wp->w_height - 2)                /* not too far off */
  233.             {
  234.                 lastline = wp->w_lsize_lnum[0] - 1;
  235.                 i = plines_m_win(wp, wp->w_topline, lastline);
  236.                 if (i < wp->w_height - 2)        /* less than a screen off */
  237.                 {
  238.                     /*
  239.                      * Try to insert the correct number of lines.
  240.                      * If not the last window, delete the lines at the bottom.
  241.                      * win_ins_lines may fail.
  242.                      */
  243.                     if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK &&
  244.                                                     wp->w_lsize_valid)
  245.                     {
  246.                         endrow = i;
  247.  
  248.                         if ((wp->w_lsize_valid += j) > wp->w_height)
  249.                             wp->w_lsize_valid = wp->w_height;
  250.                         for (idx = wp->w_lsize_valid; idx - j >= 0; idx--)
  251.                         {
  252.                             wp->w_lsize_lnum[idx] = wp->w_lsize_lnum[idx - j];
  253.                             wp->w_lsize[idx] = wp->w_lsize[idx - j];
  254.                         }
  255.                         idx = 0;
  256.                     }
  257.                 }
  258.                 else if (lastwin == firstwin)    /* far off: clearing the screen is faster */
  259.                     screenclear();
  260.             }
  261.             else if (lastwin == firstwin)    /* far off: clearing the screen is faster */
  262.                 screenclear();
  263.         }
  264.         else                            /* may scroll up */
  265.         {
  266.             j = -1;
  267.             for (i = 0; i < wp->w_lsize_valid; i++) /* try to find wp->w_topline in wp->w_lsize_lnum[] */
  268.             {
  269.                 if (wp->w_lsize_lnum[i] == wp->w_topline)
  270.                 {
  271.                     j = i;
  272.                     break;
  273.                 }
  274.                 row += wp->w_lsize[i];
  275.             }
  276.             if (j == -1)    /* wp->w_topline is not in wp->w_lsize_lnum */
  277.             {
  278.                 row = 0;
  279.                 if (lastwin == firstwin)
  280.                     screenclear();   /* far off: clearing the screen is faster */
  281.             }
  282.             else
  283.             {
  284.                 /*
  285.                  * Try to delete the correct number of lines.
  286.                  * wp->w_topline is at wp->w_lsize_lnum[i].
  287.                  */
  288.                 if ((row == 0 || win_del_lines(wp, 0, row, FALSE, wp == firstwin) == OK) && wp->w_lsize_valid)
  289.                 {
  290.                     srow = row;
  291.                     row = 0;
  292.                     for (;;)
  293.                     {
  294.                         if (type == VALID_TO_CURSCHAR && lnum == wp->w_cursor.lnum)
  295.                                 break;
  296.                         if (row + srow + (int)wp->w_lsize[j] >= wp->w_height)
  297.                                 break;
  298.                         wp->w_lsize[idx] = wp->w_lsize[j];
  299.                         wp->w_lsize_lnum[idx] = lnum++;
  300.  
  301.                         row += wp->w_lsize[idx++];
  302.                         if ((int)++j >= wp->w_lsize_valid)
  303.                             break;
  304.                     }
  305.                     wp->w_lsize_valid = idx;
  306.                 }
  307.                 else
  308.                     row = 0;        /* update all lines */
  309.             }
  310.         }
  311.         if (endrow == wp->w_height && idx == 0)     /* no scrolling */
  312.                 wp->w_lsize_valid = 0;
  313.     }
  314.  
  315.     done = didline = FALSE;
  316.     screen_start();    /* init cursor position of screen_char() */
  317.  
  318.     if (VIsual.lnum)                /* check if we are updating the inverted part */
  319.     {
  320.         linenr_t    from, to;
  321.  
  322.     /* find the line numbers that need to be updated */
  323.         if (wp->w_cursor.lnum < old_cursor.lnum)
  324.         {
  325.             from = wp->w_cursor.lnum;
  326.             to = old_cursor.lnum;
  327.         }
  328.         else
  329.         {
  330.             from = old_cursor.lnum;
  331.             to = wp->w_cursor.lnum;
  332.         }
  333.     /* if in block mode and changed column or wp->w_curswant: update all lines */
  334.         if (Visual_block && (wp->w_cursor.col != old_cursor.col || wp->w_curswant != oldCurswant))
  335.         {
  336.             if (from > VIsual.lnum)
  337.                 from = VIsual.lnum;
  338.             if (to < VIsual.lnum)
  339.                 to = VIsual.lnum;
  340.         }
  341.  
  342.         if (from < wp->w_topline)
  343.             from = wp->w_topline;
  344.         if (to >= wp->w_botline)
  345.             to = wp->w_botline - 1;
  346.  
  347.     /* find the minimal part to be updated */
  348.         if (type == INVERTED)
  349.         {
  350.             while (lnum < from)                        /* find start */
  351.             {
  352.                 row += wp->w_lsize[idx++];
  353.                 ++lnum;
  354.             }
  355.             srow = row;
  356.             for (j = idx; j < wp->w_lsize_valid; ++j)    /* find end */
  357.             {
  358.                 if (wp->w_lsize_lnum[j] == to + 1)
  359.                 {
  360.                     endrow = srow;
  361.                     break;
  362.                 }
  363.                 srow += wp->w_lsize[j];
  364.             }
  365.             old_cursor = wp->w_cursor;
  366.             oldCurswant = wp->w_curswant;
  367.         }
  368.     /* if we update the lines between from and to set old_cursor */
  369.         else if (lnum <= from && (endrow == wp->w_height || lastline >= to))
  370.         {
  371.             old_cursor = wp->w_cursor;
  372.             oldCurswant = wp->w_curswant;
  373.         }
  374.     }
  375.  
  376.     (void)set_highlight('v');
  377.  
  378.     /*
  379.      * Update the screen rows from "row" to "endrow".
  380.      * Start at line "lnum" which is at wp->w_lsize_lnum[idx].
  381.      */
  382.     for (;;)
  383.     {
  384.         if (lnum > wp->w_buffer->b_ml.ml_line_count)        /* hit the end of the file */
  385.         {
  386.             done = TRUE;
  387.             break;
  388.         }
  389.         srow = row;
  390.         row = win_line(wp, lnum, srow, endrow);
  391.         if (row > endrow)    /* past end of screen */
  392.         {
  393.             wp->w_lsize[idx] = plines_win(wp, lnum);    /* we may need the size of that */
  394.             wp->w_lsize_lnum[idx++] = lnum;        /* too long line later on */
  395.             break;
  396.         }
  397.  
  398.         wp->w_lsize[idx] = row - srow;
  399.         wp->w_lsize_lnum[idx++] = lnum;
  400.         if (++lnum > wp->w_buffer->b_ml.ml_line_count)
  401.         {
  402.             done = TRUE;
  403.             break;
  404.         }
  405.  
  406.         if (row == endrow)
  407.         {
  408.             didline = TRUE;
  409.             break;
  410.         }
  411.     }
  412.     if (idx > wp->w_lsize_valid)
  413.         wp->w_lsize_valid = idx;
  414.  
  415.     /* Do we have to do off the top of the screen processing ? */
  416.     if (endrow != wp->w_height)
  417.     {
  418.         row = 0;
  419.         for (idx = 0; idx < wp->w_lsize_valid && row < wp->w_height; idx++)
  420.             row += wp->w_lsize[idx];
  421.  
  422.         if (row < wp->w_height)
  423.         {
  424.             done = TRUE;
  425.         }
  426.         else if (row > wp->w_height)        /* Need to blank out the last line */
  427.         {
  428.             lnum = wp->w_lsize_lnum[idx - 1];
  429.             srow = row - wp->w_lsize[idx - 1];
  430.             didline = FALSE;
  431.         }
  432.         else
  433.         {
  434.             lnum = wp->w_lsize_lnum[idx - 1] + 1;
  435.             didline = TRUE;
  436.         }
  437.     }
  438.  
  439.     wp->w_empty_rows = 0;
  440.     /*
  441.      * If we didn't hit the end of the file, and we didn't finish the last
  442.      * line we were working on, then the line didn't fit.
  443.      */
  444.     if (!done && !didline)
  445.     {
  446.         if (lnum == wp->w_topline)
  447.         {
  448.             /*
  449.              * Single line that does not fit!
  450.              * Fill last line with '@' characters.
  451.              */
  452.             screen_fill(wp->w_winpos + wp->w_height - 1, wp->w_winpos + wp->w_height, 0, (int)Columns, '@', '@');
  453.             wp->w_botline = lnum + 1;
  454.         }
  455.         else
  456.         {
  457.             /*
  458.              * Clear the rest of the screen and mark the unused lines.
  459.              */
  460.             screen_fill(wp->w_winpos + srow, wp->w_winpos + wp->w_height, 0, (int)Columns, '@', ' ');
  461.             wp->w_botline = lnum;
  462.             wp->w_empty_rows = wp->w_height - srow;
  463.         }
  464.     }
  465.     else
  466.     {
  467.         /* make sure the rest of the screen is blank */
  468.         /* put '~'s on rows that aren't part of the file. */
  469.         screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height, 0, (int)Columns, '~', ' ');
  470.         wp->w_empty_rows = wp->w_height - row;
  471.  
  472.         if (done)                /* we hit the end of the file */
  473.             wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1;
  474.         else
  475.             wp->w_botline = lnum;
  476.     }
  477.  
  478.     wp->w_redr_type = 0;
  479. }
  480.  
  481. /*
  482.  * mark all status lines for redraw; used after first :cd
  483.  */
  484.     void
  485. status_redraw_all()
  486. {
  487.     WIN        *wp;
  488.  
  489.     for (wp = firstwin; wp; wp = wp->w_next)
  490.         wp->w_redr_status = TRUE;
  491.     updateScreen(NOT_VALID);
  492. }
  493.  
  494. /*
  495.  * Redraw the status line of window wp.
  496.  *
  497.  * If inversion is possible we use it. Else '=' characters are used.
  498.  */
  499.     void
  500. win_redr_status(wp)
  501.     WIN        *wp;
  502. {
  503.     int        row;
  504.     int        col;
  505.     char_u    *p;
  506.     int        len;
  507.     int        fillchar;
  508.  
  509.     if (wp->w_status_height)                    /* if there is a status line */
  510.     {
  511.         if (set_highlight('s') == OK)            /* can highlight */
  512.         {
  513.             fillchar = ' ';
  514.             start_highlight();
  515.         }
  516.         else                                    /* can't highlight, use '=' */
  517.             fillchar = '=';
  518.  
  519.         screen_start();            /* init cursor position */
  520.         row = wp->w_winpos + wp->w_height;
  521.         col = 0;
  522.         p = wp->w_buffer->b_xfilename;
  523.         if (p == NULL)
  524.             p = (char_u *)"[No File]";
  525.         else
  526.         {
  527.             home_replace(p, NameBuff, MAXPATHL);
  528.             p = NameBuff;
  529.         }
  530.         len = STRLEN(p);
  531.         if (wp->w_buffer->b_changed)
  532.             len += 4;
  533.         if (len > ru_col - 1)
  534.         {
  535.             screen_outchar('<', row, 0);
  536.             p += len - (ru_col - 1) + 1;
  537.             len = (ru_col - 1);
  538.             col = 1;
  539.         }
  540.         screen_msg(p, row, col);
  541.         if (wp->w_buffer->b_changed)
  542.             screen_msg((char_u *)" [+]", row, len - 4);
  543.         screen_fill(row, row + 1, len, ru_col, fillchar, fillchar);
  544.  
  545.         stop_highlight();
  546.         win_redr_ruler(wp, TRUE);
  547.     }
  548.     else    /* no status line, can only be last window */
  549.         redraw_cmdline = TRUE;
  550.     wp->w_redr_status = FALSE;
  551. }
  552.  
  553. /*
  554.  * display line "lnum" of window 'wp' on the screen
  555.  * Start at row "startrow", stop when "endrow" is reached.
  556.  * Return the number of last row the line occupies.
  557.  */
  558.  
  559.     static int
  560. win_line(wp, lnum, startrow, endrow)
  561.         WIN                *wp;
  562.         linenr_t        lnum;
  563.         int             startrow;
  564.         int             endrow;
  565. {
  566.     char_u             *screenp;
  567.     int                c;
  568.     int                col;                /* visual column on screen */
  569.     long            vcol;                /* visual column for tabs */
  570.     int                row;                /* row in the window, excluding w_winpos */
  571.     int                screen_row;            /* row on the screen, including w_winpos */
  572.     char_u            *ptr;
  573.     char_u            extra[16];            /* "%ld" must fit in here */
  574.     char_u            *p_extra;
  575.     int             n_extra;
  576.     int                n_spaces = 0;
  577.  
  578.     int                fromcol, tocol;        /* start/end of inverting */
  579.     int                noinvcur = FALSE;    /* don't invert the cursor */
  580.     int                temp;
  581.     FPOS            *top, *bot;
  582.  
  583.     row = startrow;
  584.     screen_row = row + wp->w_winpos;
  585.     col = 0;
  586.     vcol = 0;
  587.     fromcol = -10;
  588.     tocol = MAXCOL;
  589.     canopt = TRUE;
  590.     if (VIsual.lnum && wp == curwin)            /* visual active in this window */
  591.     {
  592.         if (ltoreq(wp->w_cursor, VIsual))        /* Visual is after wp->w_cursor */
  593.         {
  594.             top = &wp->w_cursor;
  595.             bot = &VIsual;
  596.         }
  597.         else                            /* Visual is before wp->w_cursor */
  598.         {
  599.             top = &VIsual;
  600.             bot = &wp->w_cursor;
  601.         }
  602.         if (Visual_block)                        /* block mode */
  603.         {
  604.             if (lnum >= top->lnum && lnum <= bot->lnum)
  605.             {
  606.                 fromcol = getvcol(wp, top, 2);
  607.                 temp = getvcol(wp, bot, 2);
  608.                 if (temp < fromcol)
  609.                     fromcol = temp;
  610.  
  611.                 if (wp->w_curswant != MAXCOL)
  612.                 {
  613.                     tocol = getvcol(wp, top, 3);
  614.                     temp = getvcol(wp, bot, 3);
  615.                     if (temp > tocol)
  616.                         tocol = temp;
  617.                     ++tocol;
  618.                 }
  619.             }
  620.         }
  621.         else                            /* non-block mode */
  622.         {
  623.             if (lnum > top->lnum && lnum <= bot->lnum)
  624.                 fromcol = 0;
  625.             else if (lnum == top->lnum)
  626.                 fromcol = getvcol(wp, top, 2);
  627.             if (lnum == bot->lnum)
  628.                 tocol = getvcol(wp, bot, 3) + 1;
  629.  
  630.             if (VIsual.col == VISUALLINE)        /* linewise */
  631.             {
  632.                 if (fromcol > 0)
  633.                     fromcol = 0;
  634.                 tocol = VISUALLINE;
  635.             }
  636.         }
  637.             /* if the cursor can't be switched off, don't invert the character
  638.                         where the cursor is */
  639.         if ((T_CI == NULL || *T_CI == NUL) && lnum == wp->w_cursor.lnum)
  640.             noinvcur = TRUE;
  641.  
  642.         if (tocol <= wp->w_leftcol)            /* inverting is left of screen */
  643.             fromcol = 0;
  644.         else if (fromcol >= 0 && fromcol < wp->w_leftcol)    /* start of invert is left of screen */
  645.             fromcol = wp->w_leftcol;
  646.  
  647.         /* if inverting in this line, can't optimize cursor positioning */
  648.         if (fromcol >= 0)
  649.             canopt = FALSE;
  650.     }
  651.  
  652.     ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
  653.     if (!wp->w_p_wrap)        /* advance to first character to be displayed */
  654.     {
  655.         while (vcol < wp->w_leftcol && *ptr)
  656.             vcol += chartabsize(*ptr++, vcol);
  657.         if (vcol > wp->w_leftcol)
  658.         {
  659.             n_spaces = vcol - wp->w_leftcol;    /* begin with some spaces */
  660.             vcol = wp->w_leftcol;
  661.         }
  662.     }
  663.     screenp = LinePointers[screen_row];
  664.     if (wp->w_p_nu)
  665.     {
  666.         sprintf((char *)extra, "%7ld ", (long)lnum);
  667.         p_extra = extra;
  668.         n_extra = 8;
  669.         vcol -= 8;        /* so vcol is 0 when line number has been printed */
  670.     }
  671.     else
  672.     {
  673.         p_extra = NULL;
  674.         n_extra = 0;
  675.     }
  676.     for (;;)
  677.     {
  678.         if (!canopt)    /* Visual in this line */
  679.         {
  680.             if (((vcol == fromcol && !(noinvcur && vcol == wp->w_virtcol)) ||
  681.                     (noinvcur && vcol == wp->w_virtcol + 1 && vcol >= fromcol)) &&
  682.                     vcol < tocol)
  683.                 start_highlight();        /* start highlighting */
  684.             else if (invert && (vcol == tocol || (noinvcur && vcol == wp->w_virtcol)))
  685.                 stop_highlight();        /* stop highlighting */
  686.         }
  687.  
  688.         /* Get the next character to put on the screen. */
  689.         /*
  690.          * The 'extra' array contains the extra stuff that is inserted to
  691.          * represent special characters (non-printable stuff).
  692.          */
  693.  
  694.         if (n_extra)
  695.         {
  696.             c = *p_extra++;
  697.             n_extra--;
  698.         }
  699.         else if (n_spaces)
  700.         {
  701.             c = ' ';
  702.             n_spaces--;
  703.         }
  704.         else
  705.         {
  706.             if ((c = *ptr++) < ' ' || (c > '~' && c <= 0xa0))
  707.             {
  708.                 /*
  709.                  * when getting a character from the file, we may have to turn it
  710.                  * into something else on the way to putting it into 'Nextscreen'.
  711.                  */
  712.                 if (c == TAB && !wp->w_p_list)
  713.                 {
  714.                     /* tab amount depends on current column */
  715.                     n_spaces = (int)wp->w_buffer->b_p_ts - vcol % (int)wp->w_buffer->b_p_ts - 1;
  716.                     c = ' ';
  717.                 }
  718.                 else if (c == NUL && wp->w_p_list)
  719.                 {
  720.                     p_extra = (char_u *)"";
  721.                     n_extra = 1;
  722.                     c = '$';
  723.                 }
  724.                 else if (c != NUL)
  725.                 {
  726.                     p_extra = transchar(c);
  727.                     n_extra = charsize(c) - 1;
  728.                     c = *p_extra++;
  729.                 }
  730.             }
  731.         }
  732.  
  733.         if (c == NUL)
  734.         {
  735.             if (invert)
  736.             {
  737.                 if (vcol == 0)    /* invert first char of empty line */
  738.                 {
  739.                     if (*screenp != (' ' ^ INVERTCODE))
  740.                     {
  741.                             *screenp = (' ' ^ INVERTCODE);
  742.                             screen_char(screenp, screen_row, col);
  743.                     }
  744.                     ++screenp;
  745.                     ++col;
  746.                 }
  747.                 stop_highlight();
  748.             }
  749.             /* 
  750.              * blank out the rest of this row, if necessary
  751.              */
  752.             while (col < Columns && *screenp == ' ')
  753.             {
  754.                 ++screenp;
  755.                 ++col;
  756.             }
  757.             if (col < Columns)
  758.             {
  759.                 screen_fill(screen_row, screen_row + 1, col, (int)Columns, ' ', ' ');
  760.                 col = Columns;
  761.             }
  762.             row++;
  763.             screen_row++;
  764.             break;
  765.         }
  766.         if (col >= Columns)
  767.         {
  768.             col = 0;
  769.             ++row;
  770.             ++screen_row;
  771.             if (!wp->w_p_wrap)
  772.                 break;
  773.             if (row == endrow)        /* line got too long for screen */
  774.             {
  775.                 ++row;
  776.                 break;
  777.             }
  778.             screenp = LinePointers[screen_row];
  779.         }
  780.  
  781.         /*
  782.          * Store the character in Nextscreen.
  783.          * Be careful with characters where (c ^ INVERTCODE == ' '), they may be
  784.          * confused with spaces inserted by scrolling.
  785.          */
  786.         if (*screenp != (c ^ invert) || c == (' ' ^ INVERTCODE))
  787.         {
  788.             *screenp = (c ^ invert);
  789.             screen_char(screenp, screen_row, col);
  790.         }
  791.         ++screenp;
  792.         col++;
  793.         vcol++;
  794.     }
  795.  
  796.     if (invert)
  797.         stop_highlight();
  798.     return (row);
  799. }
  800.  
  801. /*
  802.  * output a single character directly to the screen
  803.  * update NextScreen
  804.  * Note: must do screen_start() before this!
  805.  */
  806.     void
  807. screen_outchar(c, row, col)
  808.     int        c;
  809.     int        row, col;
  810. {
  811.     char_u        buf[2];
  812.  
  813.     buf[0] = c;
  814.     buf[1] = NUL;
  815.     screen_msg(buf, row, col);
  816. }
  817.     
  818. /*
  819.  * put string '*msg' on the screen at position 'row' and 'col'
  820.  * update NextScreen
  821.  * Note: only outputs within one row, message is truncated at screen boundary!
  822.  * Note: must do screen_start() before this!
  823.  * Note: caller must make sure that row is valid!
  824.  */
  825.     void
  826. screen_msg(msg, row, col)
  827.     char_u    *msg;
  828.     int        row;
  829.     int        col;
  830. {
  831.     char_u    *screenp;
  832.  
  833.     screenp = LinePointers[row] + col;
  834.     while (*msg && col < Columns)
  835.     {
  836.         if (*screenp != (*msg ^ invert) || *msg == (' ' ^ INVERTCODE))
  837.         {
  838.             *screenp = (*msg ^ invert);
  839.             screen_char(screenp, row, col);
  840.         }
  841.         ++screenp;
  842.         ++col;
  843.         ++msg;
  844.     }
  845. }
  846.  
  847. /*
  848.  * last cursor position known by screen_char
  849.  */
  850. static int    oldrow, oldcol;        /* old cursor position */
  851.  
  852. /*
  853.  * reset cursor position. Use whenever cursor moved before calling screen_char.
  854.  */
  855.     void
  856. screen_start()
  857. {
  858.     oldcol = 9999;
  859. }
  860.  
  861. /*
  862.  * set_highlight - set highlight depending on 'highlight' option and context.
  863.  *
  864.  * return FAIL if highlighting is not possible, OK otherwise
  865.  */
  866.     int
  867. set_highlight(context)
  868.     int        context;
  869. {
  870.     int        len;
  871.     int        i;
  872.     int        mode;
  873.  
  874.     len = STRLEN(p_hl);
  875.     for (i = 0; i < len; i += 3)
  876.         if (p_hl[i] == context)
  877.             break;
  878.     if (i < len)
  879.         mode = p_hl[i + 1];
  880.     else
  881.         mode = 'i';
  882.     switch (mode)
  883.     {
  884.         case 'b':    highlight = T_TB;        /* bold */
  885.                     unhighlight = T_TP;
  886.                     break;
  887.         case 's':    highlight = T_SO;        /* standout */
  888.                     unhighlight = T_SE;
  889.                     break;
  890.         case 'n':    highlight = NULL;        /* no highlighting */
  891.                     unhighlight = NULL;
  892.                     break;
  893.         default:    highlight = T_TI;        /* invert/reverse */
  894.                     unhighlight = T_TP;
  895.                     break;
  896.     }
  897.     if (highlight == NULL || *highlight == NUL ||
  898.                         unhighlight == NULL || *unhighlight == NUL)
  899.     {
  900.         highlight = NULL;
  901.         return FAIL;
  902.     }
  903.     return OK;
  904. }
  905.  
  906.     void
  907. start_highlight()
  908. {
  909.     if (highlight != NULL)
  910.     {
  911.         outstr(highlight);
  912.         invert = INVERTCODE;
  913.     }
  914. }
  915.  
  916.     void
  917. stop_highlight()
  918. {
  919.     if (invert)
  920.     {
  921.         outstr(unhighlight);
  922.         invert = 0;
  923.     }
  924. }
  925.  
  926. /*
  927.  * put character '*p' on the screen at position 'row' and 'col'
  928.  */
  929.     static void
  930. screen_char(p, row, col)
  931.         char_u    *p;
  932.         int     row;
  933.         int     col;
  934. {
  935.     int            c;
  936.     int            noinvcurs;
  937.  
  938.     /*
  939.      * Outputting the last character on the screen may scrollup the screen.
  940.      * Don't to it!
  941.      */
  942.     if (row == Rows - 1 && col == Columns - 1)
  943.         return;
  944.     if (oldcol != col || oldrow != row)
  945.     {
  946.         /* check if no cursor movement is allowed in standout mode */
  947.         if (invert && !p_wi && (T_MS == NULL || *T_MS == NUL))
  948.             noinvcurs = 7;
  949.         else
  950.             noinvcurs = 0;
  951.  
  952.         /*
  953.          * If we're on the same row (which happens a lot!), try to
  954.          * avoid a windgoto().
  955.          * If we are only a few characters off, output the
  956.          * characters. That is faster than cursor positioning.
  957.          * This can't be used when inverting (a part of) the line.
  958.          */
  959.         if (oldrow == row && oldcol < col)
  960.         {
  961.             register int i;
  962.  
  963.             i = col - oldcol;
  964.             if (i <= 4 + noinvcurs && canopt)
  965.             {
  966.                 while (i)
  967.                 {
  968.                     c = *(p - i--);
  969.                     outchar(c ^ invert);
  970.                 }
  971.             }
  972.             else
  973.             {
  974.                 if (noinvcurs)
  975.                     stop_highlight();
  976.             
  977.                 if (T_CRI && *T_CRI)    /* use tgoto interface! jw */
  978.                     OUTSTR(tgoto((char *)T_CRI, 0, i));
  979.                 else
  980.                     windgoto(row, col);
  981.             
  982.                 if (noinvcurs)
  983.                     start_highlight();
  984.             }
  985.             oldcol = col;
  986.         }
  987.         else
  988.         {
  989.             if (noinvcurs)
  990.                 stop_highlight();
  991.             windgoto(oldrow = row, oldcol = col);
  992.             if (noinvcurs)
  993.                 start_highlight();
  994.         }
  995.     }
  996.     /*
  997.      * For weird invert mechanism: output (un)highlight before every char
  998.      * Lots of extra output, but works.
  999.      */
  1000.     if (p_wi)
  1001.     {
  1002.         if (invert)                                      
  1003.             outstr(highlight);                            
  1004.         else                                             
  1005.             outstr(unhighlight);
  1006.     }
  1007.     outchar(*p ^ invert);
  1008.     oldcol++;
  1009. }
  1010.  
  1011. /*
  1012.  * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col'
  1013.  * with character 'c1' in first column followed by 'c2' in the other columns.
  1014.  */
  1015.     void
  1016. screen_fill(start_row, end_row, start_col, end_col, c1, c2)
  1017.     int     start_row, end_row;
  1018.     int        start_col, end_col;
  1019.     int        c1, c2;
  1020. {
  1021.     int                row;
  1022.     int                col;
  1023.     char_u            *screenp;
  1024.     int                did_delete = FALSE;
  1025.     int                c;
  1026.  
  1027.     if (start_row >= end_row || start_col >= end_col)    /* nothing to do */
  1028.         return;
  1029.  
  1030.     c1 ^= invert;
  1031.     c2 ^= invert;
  1032.     for (row = start_row; row < end_row; ++row)
  1033.     {
  1034.             /* try to use delete-line termcap code */
  1035.         if (c2 == ' ' && end_col == Columns && T_EL != NULL && *T_EL != NUL)
  1036.         {
  1037.             /*
  1038.              * check if we really need to clear something
  1039.              */
  1040.             col = start_col;
  1041.             screenp = LinePointers[row] + start_col;
  1042.             if (c1 != ' ')                        /* don't clear first char */
  1043.             {
  1044.                 ++col;
  1045.                 ++screenp;
  1046.             }
  1047.             while (col < end_col && *screenp == ' ')    /* skip blanks */
  1048.             {
  1049.                 ++col;
  1050.                 ++screenp;
  1051.             }
  1052.             if (col < end_col)                    /* something to be cleared */
  1053.             {
  1054.                 windgoto(row, col);
  1055.                 outstr(T_EL);
  1056.             }
  1057.             did_delete = TRUE;
  1058.         }
  1059.  
  1060.         screen_start();            /* init cursor position of screen_char() */
  1061.         screenp = LinePointers[row] + start_col;
  1062.         c = c1;
  1063.         for (col = start_col; col < end_col; ++col)
  1064.         {
  1065.             if (*screenp != c)
  1066.             {
  1067.                 *screenp = c;
  1068.                 if (!did_delete || c != ' ')
  1069.                     screen_char(screenp, row, col);
  1070.             }
  1071.             ++screenp;
  1072.             c = c2;
  1073.         }
  1074.         if (row == Rows - 1)
  1075.         {
  1076.             redraw_cmdline = TRUE;
  1077.             if (c1 == ' ' && c2 == ' ')
  1078.                 clear_cmdline = FALSE;
  1079.         }
  1080.     }
  1081. }
  1082.  
  1083. /*
  1084.  * recompute all w_botline's. Called after Rows changed.
  1085.  */
  1086.     void
  1087. comp_Botline_all()
  1088. {
  1089.     WIN        *wp;
  1090.  
  1091.     for (wp = firstwin; wp; wp = wp->w_next)
  1092.         comp_Botline(wp);
  1093. }
  1094.  
  1095. /*
  1096.  * compute wp->w_botline. Can be called after wp->w_topline changed.
  1097.  */
  1098.     void
  1099. comp_Botline(wp)
  1100.     WIN            *wp;
  1101. {
  1102.     linenr_t    lnum;
  1103.     int            done = 0;
  1104.  
  1105.     for (lnum = wp->w_topline; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
  1106.     {
  1107.         if ((done += plines_win(wp, lnum)) > wp->w_height)
  1108.             break;
  1109.     }
  1110.     wp->w_botline = lnum;        /* wp->w_botline is the line that is just below the window */
  1111. }
  1112.  
  1113.     static void
  1114. screenalloc(clear)
  1115.     int        clear;
  1116. {
  1117.     static int        old_Rows = 0;
  1118.     static int        old_Columns = 0;
  1119.     register int    i;
  1120.     WIN                *wp;
  1121.     int                outofmem = FALSE;
  1122.  
  1123.     /*
  1124.      * Allocation of the screen buffers is done only when the size changes
  1125.      * and when Rows and Columns have been set.
  1126.      */
  1127.     if ((Nextscreen != NULL && Rows == old_Rows && Columns == old_Columns) || Rows == 0 || Columns == 0)
  1128.         return;
  1129.  
  1130.     comp_col();            /* recompute columns for shown command and ruler */
  1131.     old_Rows = Rows;
  1132.     old_Columns = Columns;
  1133.  
  1134.     /*
  1135.      * If we're changing the size of the screen, free the old arrays
  1136.      */
  1137.     free(Nextscreen);
  1138.     free(LinePointers);
  1139.     for (wp = firstwin; wp; wp = wp->w_next)
  1140.         win_free_lsize(wp);
  1141.  
  1142.     Nextscreen = (char_u *)malloc((size_t) (Rows * Columns));
  1143.     LinePointers = (char_u **)malloc(sizeof(char_u *) * Rows);
  1144.     for (wp = firstwin; wp; wp = wp->w_next)
  1145.     {
  1146.         if (win_alloc_lsize(wp) == FAIL)
  1147.         {
  1148.             outofmem = TRUE;
  1149.             break;
  1150.         }
  1151.     }
  1152.  
  1153.     if (Nextscreen == NULL || LinePointers == NULL || outofmem)
  1154.     {
  1155.         emsg(e_outofmem);
  1156.         free(Nextscreen);
  1157.         Nextscreen = NULL;
  1158.     }
  1159.     else
  1160.     {
  1161.         for (i = 0; i < Rows; ++i)
  1162.             LinePointers[i] = Nextscreen + i * Columns;
  1163.     }
  1164.  
  1165.     if (clear)
  1166.         screenclear2();
  1167. }
  1168.  
  1169.     void
  1170. screenclear()
  1171. {
  1172.     screenalloc(FALSE);            /* allocate screen buffers if size changed */
  1173.     screenclear2();
  1174. }
  1175.  
  1176.     static void
  1177. screenclear2()
  1178. {
  1179.     if (starting || Nextscreen == NULL)
  1180.         return;
  1181.  
  1182.     outstr(T_ED);                /* clear the display */
  1183.  
  1184.                                 /* blank out Nextscreen */
  1185.     memset((char *)Nextscreen, ' ', (size_t)(Rows * Columns));
  1186.  
  1187.     win_rest_invalid(firstwin);
  1188.     clear_cmdline = FALSE;
  1189.     if (must_redraw == CLEAR)        /* no need to clear again */
  1190.         must_redraw = NOT_VALID;
  1191. }
  1192.  
  1193. /*
  1194.  * check cursor for a valid lnum
  1195.  */
  1196.     void
  1197. check_cursor()
  1198. {
  1199.     if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
  1200.         curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
  1201.     if (curwin->w_cursor.lnum <= 0)
  1202.         curwin->w_cursor.lnum = 1;
  1203. }
  1204.  
  1205.     void
  1206. cursupdate()
  1207. {
  1208.     linenr_t        p;
  1209.     long             nlines;
  1210.     int             i;
  1211.     int             temp;
  1212.  
  1213.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  1214.  
  1215.     if (Nextscreen == NULL)
  1216.         return;
  1217.  
  1218.     check_cursor();
  1219.     if (bufempty())             /* special case - file is empty */
  1220.     {
  1221.         curwin->w_topline = 1;
  1222.         curwin->w_cursor.lnum = 1;
  1223.         curwin->w_cursor.col = 0;
  1224.         curwin->w_lsize[0] = 0;
  1225.         if (curwin->w_lsize_valid == 0)        /* don't know about screen contents */
  1226.             updateScreen(NOT_VALID);
  1227.         curwin->w_lsize_valid = 1;
  1228.     }
  1229.     else if (curwin->w_cursor.lnum < curwin->w_topline)
  1230.     {
  1231. /*
  1232.  * If the cursor is above the top of the screen, scroll the screen to
  1233.  * put it at the top of the screen.
  1234.  * If we weren't very close to begin with, we scroll more, so that
  1235.  * the line is close to the middle.
  1236.  */
  1237.         temp = curwin->w_height / 2 - 1;
  1238.         if (temp < 2)
  1239.             temp = 2;
  1240.         if (curwin->w_topline - curwin->w_cursor.lnum >= temp)        /* not very close */
  1241.         {
  1242.             p = curwin->w_cursor.lnum;
  1243.             i = plines(p);
  1244.             temp += i;
  1245.                                 /* count lines for 1/2 screenheight */
  1246.             while (i < curwin->w_height + 1 && i < temp && p > 1)
  1247.                 i += plines(--p);
  1248.             curwin->w_topline = p;
  1249.             if (i > curwin->w_height)        /* cursor line won't fit, backup one line */
  1250.                 ++curwin->w_topline;
  1251.         }
  1252.         else if (p_sj > 1)        /* scroll at least p_sj lines */
  1253.         {
  1254.             for (i = 0; i < p_sj && curwin->w_topline > 1; i += plines(--curwin->w_topline))
  1255.                 ;
  1256.         }
  1257.         if (curwin->w_topline > curwin->w_cursor.lnum)
  1258.             curwin->w_topline = curwin->w_cursor.lnum;
  1259.         updateScreen(VALID);
  1260.     }
  1261.     else if (curwin->w_cursor.lnum >= curwin->w_botline)
  1262.     {
  1263. /*
  1264.  * If the cursor is below the bottom of the screen, scroll the screen to
  1265.  * put the cursor on the screen.
  1266.  * If the cursor is less than a screenheight down
  1267.  * compute the number of lines at the top which have the same or more
  1268.  * rows than the rows of the lines below the bottom
  1269.  */
  1270.         nlines = curwin->w_cursor.lnum - curwin->w_botline + 1;
  1271.         if (nlines <= curwin->w_height + 1)
  1272.         {
  1273.                 /* get the number or rows to scroll minus the number of
  1274.                                 free '~' rows */
  1275.             temp = plines_m(curwin->w_botline, curwin->w_cursor.lnum) - curwin->w_empty_rows;
  1276.             if (temp <= 0)                /* curwin->w_empty_rows is larger, no need to scroll */
  1277.                 nlines = 0;
  1278.             else if (temp > curwin->w_height)        /* more than a screenfull, don't scroll */
  1279.                 nlines = temp;
  1280.             else
  1281.             {
  1282.                     /* scroll minimal number of lines */
  1283.                 if (temp < p_sj)
  1284.                     temp = p_sj;
  1285.                 for (i = 0, p = curwin->w_topline; i < temp && p < curwin->w_botline; ++p)
  1286.                     i += plines(p);
  1287.                 if (i >= temp)                /* it's possible to scroll */
  1288.                     nlines = p - curwin->w_topline;
  1289.                 else                        /* below curwin->w_botline, don't scroll */
  1290.                     nlines = 9999;
  1291.             }
  1292.         }
  1293.  
  1294.         /*
  1295.          * Scroll up if the cursor is off the bottom of the screen a bit.
  1296.          * Otherwise put it at 1/2 of the screen.
  1297.          */
  1298.         if (nlines >= curwin->w_height / 2 && nlines > p_sj)
  1299.         {
  1300.             p = curwin->w_cursor.lnum;
  1301.             temp = curwin->w_height / 2 + 1;
  1302.             nlines = 0;
  1303.             i = 0;
  1304.             do                /* this loop could win a contest ... */
  1305.                 i += plines(p);
  1306.             while (i < temp && (nlines = 1) != 0 && --p != 0);
  1307.             curwin->w_topline = p + nlines;
  1308.         }
  1309.         else
  1310.             scrollup(nlines);
  1311.         updateScreen(VALID);
  1312.     }
  1313.     else if (curwin->w_lsize_valid == 0)        /* don't know about screen contents */
  1314.         updateScreen(NOT_VALID);
  1315.     curwin->w_row = curwin->w_col = curwin->w_virtcol = i = 0;
  1316.     for (p = curwin->w_topline; p != curwin->w_cursor.lnum; ++p)
  1317.         if (RedrawingDisabled)        /* curwin->w_lsize[] invalid */
  1318.             curwin->w_row += plines(p);
  1319.         else
  1320.             curwin->w_row += curwin->w_lsize[i++];
  1321.  
  1322.     Cline_row = curwin->w_row;
  1323.     if (!RedrawingDisabled && i > curwin->w_lsize_valid)
  1324.                                 /* Should only happen with a line that is too */
  1325.                                 /* long to fit on the last screen line. */
  1326.         Cline_height = 0;
  1327.     else
  1328.     {
  1329.         if (RedrawingDisabled)              /* curwin->w_lsize[] invalid */
  1330.             Cline_height = plines(curwin->w_cursor.lnum);
  1331.         else
  1332.             Cline_height = curwin->w_lsize[i];
  1333.  
  1334.         curs_columns(!RedrawingDisabled);    /* compute curwin->w_virtcol and curwin->w_col */
  1335.         if (must_redraw)
  1336.             updateScreen(must_redraw);
  1337.     }
  1338.  
  1339.     if (curwin->w_set_curswant)
  1340.     {
  1341.         curwin->w_curswant = curwin->w_virtcol;
  1342.         curwin->w_set_curswant = FALSE;
  1343.     }
  1344. }
  1345.  
  1346. /*
  1347.  * compute curwin->w_col and curwin->w_virtcol
  1348.  */
  1349.     void
  1350. curs_columns(scroll)
  1351.     int scroll;            /* when TRUE, may scroll horizontally */
  1352. {
  1353.     int diff;
  1354.  
  1355.     curwin->w_virtcol = getvcol(curwin, &curwin->w_cursor, 1);
  1356.     curwin->w_col = curwin->w_virtcol;
  1357.     if (curwin->w_p_nu)
  1358.         curwin->w_col += 8;
  1359.  
  1360.     curwin->w_row = Cline_row;
  1361.     if (curwin->w_p_wrap)            /* long line wrapping, adjust curwin->w_row */
  1362.         while (curwin->w_col >= Columns)
  1363.         {
  1364.             curwin->w_col -= Columns;
  1365.             curwin->w_row++;
  1366.         }
  1367.     else if (scroll)    /* no line wrapping, compute curwin->w_leftcol if scrolling is on */
  1368.                         /* if scrolling is off, curwin->w_leftcol is assumed to be 0 */
  1369.     {
  1370.                         /* If Cursor is in columns 0, start in column 0 */
  1371.                         /* If Cursor is left of the screen, scroll rightwards */
  1372.                         /* If Cursor is right of the screen, scroll leftwards */
  1373.         if (curwin->w_cursor.col == 0)
  1374.         {
  1375.                         /* screen has to be redrawn with new curwin->w_leftcol */
  1376.             if (curwin->w_leftcol != 0 && must_redraw < NOT_VALID)
  1377.                 must_redraw = NOT_VALID;
  1378.             curwin->w_leftcol = 0;
  1379.         }
  1380.         else if (((diff = curwin->w_leftcol + (curwin->w_p_nu ? 8 : 0)
  1381.                     - curwin->w_col) > 0 ||
  1382.                     (diff = curwin->w_col - (curwin->w_leftcol + Columns) + 1) > 0))
  1383.         {
  1384.             if (p_ss == 0 || diff >= Columns / 2)
  1385.                 curwin->w_leftcol = curwin->w_col - Columns / 2;
  1386.             else
  1387.             {
  1388.                 if (diff < p_ss)
  1389.                     diff = p_ss;
  1390.                 if (curwin->w_col < curwin->w_leftcol + 8)
  1391.                     curwin->w_leftcol -= diff;
  1392.                 else
  1393.                     curwin->w_leftcol += diff;
  1394.             }
  1395.             if (curwin->w_leftcol < 0)
  1396.                 curwin->w_leftcol = 0;
  1397.             if (must_redraw < NOT_VALID)
  1398.                 must_redraw = NOT_VALID;    /* screen has to be redrawn with new curwin->w_leftcol */
  1399.         }
  1400.         curwin->w_col -= curwin->w_leftcol;
  1401.     }
  1402.     if (curwin->w_row > curwin->w_height - 1)    /* Cursor past end of screen */
  1403.         curwin->w_row = curwin->w_height - 1;    /* happens with line that does not fit on screen */
  1404. }
  1405.  
  1406. /*
  1407.  * get virtual column number of pos
  1408.  * type = 1: where the cursor is on this character
  1409.  * type = 2: on the first position of this character (TAB)
  1410.  * type = 3: on the last position of this character (TAB)
  1411.  */
  1412.     int
  1413. getvcol(wp, pos, type)
  1414.     WIN        *wp;
  1415.     FPOS    *pos;
  1416.     int        type;
  1417. {
  1418.     int                col;
  1419.     int                vcol;
  1420.     char_u           *ptr;
  1421.     int             incr;
  1422.     int                c;
  1423.  
  1424.     vcol = 0;
  1425.     ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE);
  1426.     for (col = pos->col; col >= 0; --col)
  1427.     {
  1428.         c = *ptr++;
  1429.         if (c == NUL)        /* make sure we don't go past the end of the line */
  1430.             break;
  1431.  
  1432.         /* A tab gets expanded, depending on the current column */
  1433.         incr = chartabsize(c, (long)vcol);
  1434.  
  1435.         if (col == 0)        /* character at pos.col */
  1436.         {
  1437.             if (type == 3 || (type == 1 && c == TAB && State == NORMAL && !wp->w_p_list))
  1438.                 --incr;
  1439.             else
  1440.                 break;
  1441.         }
  1442.         vcol += incr;
  1443.     }
  1444.     return vcol;
  1445. }
  1446.  
  1447.     void
  1448. scrolldown(nlines)
  1449.     long    nlines;
  1450. {
  1451.     register long    done = 0;    /* total # of physical lines done */
  1452.  
  1453.     /* Scroll up 'nlines' lines. */
  1454.     while (nlines--)
  1455.     {
  1456.         if (curwin->w_topline == 1)
  1457.             break;
  1458.         done += plines(--curwin->w_topline);
  1459.     }
  1460.     /*
  1461.      * Compute the row number of the last row of the cursor line
  1462.      * and move it onto the screen.
  1463.      */
  1464.     curwin->w_row += done;
  1465.     if (curwin->w_p_wrap)
  1466.         curwin->w_row += plines(curwin->w_cursor.lnum) - 1 - curwin->w_virtcol / Columns;
  1467.     while (curwin->w_row >= curwin->w_height && curwin->w_cursor.lnum > 1)
  1468.         curwin->w_row -= plines(curwin->w_cursor.lnum--);
  1469. }
  1470.  
  1471.     void
  1472. scrollup(nlines)
  1473.     long    nlines;
  1474. {
  1475. #ifdef NEVER
  1476.     register long    done = 0;    /* total # of physical lines done */
  1477.  
  1478.     /* Scroll down 'nlines' lines. */
  1479.     while (nlines--)
  1480.     {
  1481.         if (curwin->w_topline == curbuf->b_ml.ml_line_count)
  1482.             break;
  1483.         done += plines(curwin->w_topline);
  1484.         if (curwin->w_cursor.lnum == curwin->w_topline)
  1485.             ++curwin->w_cursor.lnum;
  1486.         ++curwin->w_topline;
  1487.     }
  1488.     win_del_lines(curwin, 0, done, TRUE, TRUE);
  1489. #endif
  1490.     curwin->w_topline += nlines;
  1491.     if (curwin->w_topline > curbuf->b_ml.ml_line_count)
  1492.         curwin->w_topline = curbuf->b_ml.ml_line_count;
  1493.     if (curwin->w_cursor.lnum < curwin->w_topline)
  1494.         curwin->w_cursor.lnum = curwin->w_topline;
  1495. }
  1496.  
  1497. /*
  1498.  * insert 'nlines' lines at 'row' in window 'wp'
  1499.  * if 'invalid' is TRUE the wp->w_lsize_lnum[] is invalidated.
  1500.  * if 'mayclear' is TRUE the screen will be cleared if it is faster than scrolling
  1501.  * Returns FAIL if the lines are not inserted, OK for success.
  1502.  */
  1503.     int
  1504. win_ins_lines(wp, row, nlines, invalid, mayclear)
  1505.     WIN        *wp;
  1506.     int        row;
  1507.     int        nlines;
  1508.     int        invalid;
  1509.     int        mayclear;
  1510. {
  1511.     int        did_delete;
  1512.     int        nextrow;
  1513.     int        lastrow;
  1514.     int        retval;
  1515.  
  1516.     if (invalid)
  1517.         wp->w_lsize_valid = 0;
  1518.  
  1519.     if (RedrawingDisabled || nlines <= 0 || wp->w_height < 5)
  1520.         return FAIL;
  1521.     
  1522.     if (nlines > wp->w_height - row)
  1523.         nlines = wp->w_height - row;
  1524.  
  1525.     if (mayclear && Rows - nlines < 5)    /* only a few lines left: redraw is faster */
  1526.     {
  1527.         screenclear();        /* will set wp->w_lsize_valid to 0 */
  1528.         return FAIL;
  1529.     }
  1530.  
  1531.     if (nlines == wp->w_height)    /* will delete all lines */
  1532.         return FAIL;
  1533.  
  1534.     /*
  1535.      * when scrolling, the message on the command line should be cleared,
  1536.      * otherwise it will stay there forever.
  1537.      */
  1538.     clear_cmdline = TRUE;
  1539.  
  1540.     /*
  1541.      * if the terminal can set a scroll region, use that
  1542.      */
  1543.     if (scroll_region)
  1544.     {
  1545.         scroll_region_set(wp);
  1546.         retval = screen_ins_lines(wp->w_winpos, row, nlines, wp->w_height);
  1547.         scroll_region_reset();
  1548.         return retval;
  1549.     }
  1550.  
  1551.     if (wp->w_next && p_tf)        /* don't delete/insert on fast terminal */
  1552.         return FAIL;
  1553.  
  1554.     /*
  1555.      * If there is a next window or a status line, we first try to delete the
  1556.      * lines at the bottom to avoid messing what is after the window.
  1557.      * If this fails and there are following windows, don't do anything to avoid
  1558.      * messing up those windows, better just redraw.
  1559.      */
  1560.     did_delete = FALSE;
  1561.     if (wp->w_next || wp->w_status_height)
  1562.     {
  1563.         if (screen_del_lines(0, wp->w_winpos + wp->w_height - nlines, nlines, (int)Rows) == OK)
  1564.             did_delete = TRUE;
  1565.         else if (wp->w_next)
  1566.             return FAIL;
  1567.     }
  1568.     /*
  1569.      * if no lines deleted, blank the lines that will end up below the window
  1570.      */
  1571.     if (!did_delete)
  1572.     {
  1573.         wp->w_redr_status = TRUE;
  1574.         redraw_cmdline = TRUE;
  1575.         nextrow = wp->w_winpos + wp->w_height + wp->w_status_height;
  1576.         lastrow = nextrow + nlines;
  1577.         if (lastrow > Rows)
  1578.             lastrow = Rows;
  1579.         screen_fill(nextrow - nlines, lastrow - nlines, 0, (int)Columns, ' ', ' ');
  1580.     }
  1581.  
  1582.     if (screen_ins_lines(0, wp->w_winpos + row, nlines, (int)Rows) == FAIL)
  1583.     {
  1584.             /* deletion will have messed up other windows */
  1585.         if (did_delete)
  1586.         {
  1587.             wp->w_redr_status = TRUE;
  1588.             win_rest_invalid(wp->w_next);
  1589.         }
  1590.         return FAIL;
  1591.     }
  1592.  
  1593.     return OK;
  1594. }
  1595.  
  1596. /*
  1597.  * delete 'nlines' lines at 'row' in window 'wp'
  1598.  * If 'invalid' is TRUE curwin->w_lsize_lnum[] is invalidated.
  1599.  * If 'mayclear' is TRUE the screen will be cleared if it is faster than scrolling
  1600.  * Return OK for success, FAIL if the lines are not deleted.
  1601.  */
  1602.     int
  1603. win_del_lines(wp, row, nlines, invalid, mayclear)
  1604.     WIN                *wp;
  1605.     int             row;
  1606.     int             nlines;
  1607.     int                invalid;
  1608.     int                mayclear;
  1609. {
  1610.     int            retval;
  1611.  
  1612.     if (invalid)
  1613.         wp->w_lsize_valid = 0;
  1614.  
  1615.     if (RedrawingDisabled || nlines <= 0)
  1616.         return FAIL;
  1617.     
  1618.     if (nlines > wp->w_height - row)
  1619.         nlines = wp->w_height - row;
  1620.  
  1621.     if (mayclear && Rows - nlines < 5)    /* only a few lines left: redraw is faster */
  1622.     {
  1623.         screenclear();        /* will set wp->w_lsize_valid to 0 */
  1624.         return FAIL;
  1625.     }
  1626.  
  1627.     if (nlines == wp->w_height)    /* will delete all lines */
  1628.         return FAIL;
  1629.  
  1630.     /*
  1631.      * when scrolling, the message on the command line should be cleared,
  1632.      * otherwise it will stay there forever.
  1633.      */
  1634.     clear_cmdline = TRUE;
  1635.  
  1636.     /*
  1637.      * if the terminal can set a scroll region, use that
  1638.      */
  1639.     if (scroll_region)
  1640.     {
  1641.         scroll_region_set(wp);
  1642.         retval = screen_del_lines(wp->w_winpos, row, nlines, wp->w_height);
  1643.         scroll_region_reset();
  1644.         return retval;
  1645.     }
  1646.  
  1647.     if (wp->w_next && p_tf)        /* don't delete/insert on fast terminal */
  1648.         return FAIL;
  1649.  
  1650.     if (screen_del_lines(0, wp->w_winpos + row, nlines, (int)Rows) == FAIL)
  1651.         return FAIL;
  1652.  
  1653.     /*
  1654.      * If there are windows or status lines below, try to put them at the
  1655.      * correct place. If we can't do that, they have to be redrawn.
  1656.      */
  1657.     if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
  1658.     {
  1659.         if (screen_ins_lines(0, wp->w_winpos + wp->w_height - nlines, nlines, (int)Rows) == FAIL)
  1660.         {
  1661.             wp->w_redr_status = TRUE;
  1662.             win_rest_invalid(wp->w_next);
  1663.         }
  1664.     }
  1665.     /*
  1666.      * If this is the last window and there is no status line, redraw the
  1667.      * command line later.
  1668.      */
  1669.     else
  1670.         redraw_cmdline = TRUE;
  1671.     return OK;
  1672. }
  1673.  
  1674. /*
  1675.  * window 'wp' and everything after it is messed up, mark it for redraw
  1676.  */
  1677.     void
  1678. win_rest_invalid(wp)
  1679.     WIN            *wp;
  1680. {
  1681.     while (wp)
  1682.     {
  1683.         wp->w_lsize_valid = 0;
  1684.         wp->w_redr_type = NOT_VALID;
  1685.         wp->w_redr_status = TRUE;
  1686.         wp = wp->w_next;
  1687.     }
  1688.     redraw_cmdline = TRUE;
  1689. }
  1690.  
  1691. /*
  1692.  * The rest of the routines in this file perform screen manipulations. The
  1693.  * given operation is performed physically on the screen. The corresponding
  1694.  * change is also made to the internal screen image. In this way, the editor
  1695.  * anticipates the effect of editing changes on the appearance of the screen.
  1696.  * That way, when we call screenupdate a complete redraw isn't usually
  1697.  * necessary. Another advantage is that we can keep adding code to anticipate
  1698.  * screen changes, and in the meantime, everything still works.
  1699.  */
  1700.  
  1701. /*
  1702.  * insert lines on the screen and update Nextscreen
  1703.  * 'end' is the line after the scrolled part. Normally it is Rows.
  1704.  * When scrolling region used 'off' is the offset from the top for the region.
  1705.  * 'row' and 'end' are relative to the start of the region.
  1706.  *
  1707.  * return FAIL for failure, OK for success.
  1708.  */
  1709.     static int
  1710. screen_ins_lines(off, row, nlines, end)
  1711.     int            off;
  1712.     int         row;
  1713.     int         nlines;
  1714.     int            end;
  1715. {
  1716.     int         i;
  1717.     int         j;
  1718.     char_u        *temp;
  1719.     int            cursor_row;
  1720.  
  1721.     if (T_CSC != NULL && *T_CSC != NUL)        /* cursor relative to region */
  1722.         cursor_row = row;
  1723.     else
  1724.         cursor_row = row + off;
  1725.  
  1726.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  1727.     if (Nextscreen == NULL)
  1728.         return FAIL;
  1729.  
  1730.     if (nlines <= 0 ||  ((T_CIL == NULL || *T_CIL == NUL) &&
  1731.                         (T_IL == NULL || *T_IL == NUL) &&
  1732.                         (T_SR == NULL || *T_SR == NUL || row != 0)))
  1733.         return FAIL;
  1734.     
  1735.     /*
  1736.      * It "looks" better if we do all the inserts at once
  1737.      */
  1738.     if (T_CIL && *T_CIL) 
  1739.     {
  1740.         windgoto(cursor_row, 0);
  1741.         if (nlines == 1 && T_IL && *T_IL)
  1742.             outstr(T_IL);
  1743.         else
  1744.             OUTSTR(tgoto((char *)T_CIL, 0, nlines));
  1745.     }
  1746.     else
  1747.     {
  1748.         for (i = 0; i < nlines; i++) 
  1749.         {
  1750.             if (i == 0 || cursor_row != 0)
  1751.                 windgoto(cursor_row, 0);
  1752.             if (T_IL && *T_IL)
  1753.                 outstr(T_IL);
  1754.             else
  1755.                 outstr(T_SR);
  1756.         }
  1757.     }
  1758.     /*
  1759.      * Now shift LinePointers nlines down to reflect the inserted lines.
  1760.      * Clear the inserted lines.
  1761.      */
  1762.     row += off;
  1763.     end += off;
  1764.     for (i = 0; i < nlines; ++i)
  1765.     {
  1766.         j = end - 1 - i;
  1767.         temp = LinePointers[j];
  1768.         while ((j -= nlines) >= row)
  1769.                 LinePointers[j + nlines] = LinePointers[j];
  1770.         LinePointers[j + nlines] = temp;
  1771.         memset((char *)temp, ' ', (size_t)Columns);
  1772.     }
  1773.     return OK;
  1774. }
  1775.  
  1776. /*
  1777.  * delete lines on the screen and update Nextscreen
  1778.  * 'end' is the line after the scrolled part. Normally it is Rows.
  1779.  * When scrolling region used 'off' is the offset from the top for the region.
  1780.  * 'row' and 'end' are relative to the start of the region.
  1781.  *
  1782.  * Return OK for success, FAIL if the lines are not deleted.
  1783.  */
  1784.     int
  1785. screen_del_lines(off, row, nlines, end)
  1786.     int                off;
  1787.     int             row;
  1788.     int             nlines;
  1789.     int                end;
  1790. {
  1791.     int         j;
  1792.     int         i;
  1793.     char_u        *temp;
  1794.     int            cursor_row;
  1795.     int            cursor_end;
  1796.  
  1797.     if (T_CSC != NULL && *T_CSC != NUL)        /* cursor relative to region */
  1798.     {
  1799.         cursor_row = row;
  1800.         cursor_end = end;
  1801.     }
  1802.     else
  1803.     {
  1804.         cursor_row = row + off;
  1805.         cursor_end = end + off;
  1806.     }
  1807.  
  1808.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  1809.     if (Nextscreen == NULL)
  1810.         return FAIL;
  1811.  
  1812.     if (nlines <= 0 ||  ((T_DL == NULL || *T_DL == NUL) &&
  1813.                         (T_CDL == NULL || *T_CDL == NUL) &&
  1814.                         row != 0))
  1815.         return FAIL;
  1816.  
  1817.     /* delete the lines */
  1818.     if (T_CDL && *T_CDL) 
  1819.     {
  1820.         windgoto(cursor_row, 0);
  1821.         if (nlines == 1 && T_DL && *T_DL)
  1822.             outstr(T_DL);
  1823.         else
  1824.             OUTSTR(tgoto((char *)T_CDL, 0, nlines));
  1825.     } 
  1826.     else
  1827.     {
  1828.         if (row == 0)
  1829.         {
  1830.             windgoto(cursor_end - 1, 0);
  1831.             for (i = 0; i < nlines; i++) 
  1832.                 outchar('\n');
  1833.         }
  1834.         else
  1835.         {
  1836.             for (i = 0; i < nlines; i++) 
  1837.             {
  1838.                 windgoto(cursor_row, 0);
  1839.                 outstr(T_DL);           /* delete a line */
  1840.             }
  1841.         }
  1842.     }
  1843.  
  1844.     /*
  1845.      * Now shift LinePointers nlines up to reflect the deleted lines.
  1846.      * Clear the deleted lines.
  1847.      */
  1848.     row += off;
  1849.     end += off;
  1850.     for (i = 0; i < nlines; ++i)
  1851.     {
  1852.         j = row + i;
  1853.         temp = LinePointers[j];
  1854.         while ((j += nlines) <= end - 1)
  1855.             LinePointers[j - nlines] = LinePointers[j];
  1856.         LinePointers[j - nlines] = temp;
  1857.         memset((char *)temp, ' ', (size_t)Columns);
  1858.     }
  1859.     return OK;
  1860. }
  1861.  
  1862. /*
  1863.  * show the current mode and ruler
  1864.  *
  1865.  * If clear_cmdline is TRUE, clear it first.
  1866.  * If clear_cmdline is FALSE there may be a message there that needs to be
  1867.  * cleared only if a mode is shown.
  1868.  */
  1869.     void
  1870. showmode()
  1871. {
  1872.     int        did_clear = clear_cmdline;
  1873.     int        need_clear = FALSE;
  1874.  
  1875.     if ((p_smd && (State & INSERT)) || Recording)
  1876.     {
  1877.         gotocmdline(clear_cmdline, NUL);
  1878.         if (p_smd)
  1879.         {
  1880.             if (State & INSERT)
  1881.             {
  1882.                 msg_outstr((char_u *)"-- ");
  1883.                 if (p_ri)
  1884.                     msg_outstr((char_u *)"REVERSE ");
  1885.                 if (State == INSERT)
  1886.                     msg_outstr((char_u *)"INSERT --");
  1887.                 else
  1888.                     msg_outstr((char_u *)"REPLACE --");
  1889.                 need_clear = TRUE;
  1890.             }
  1891.         }
  1892.         if (Recording)
  1893.         {
  1894.             msg_outstr((char_u *)"recording");
  1895.             need_clear = TRUE;
  1896.         }
  1897.         if (need_clear && !did_clear)
  1898.             msg_ceol();
  1899.     }
  1900.     win_redr_ruler(lastwin, TRUE);
  1901.     redraw_cmdline = FALSE;
  1902. }
  1903.  
  1904. /*
  1905.  * delete mode message
  1906.  */
  1907.     void
  1908. delmode()
  1909. {
  1910.     if (Recording)
  1911.         MSG("recording");
  1912.     else
  1913.         MSG("");
  1914. }
  1915.  
  1916. /*
  1917.  * if ruler option is set: show current cursor position
  1918.  * if always is FALSE, only print if position has changed
  1919.  */
  1920.     void
  1921. showruler(always)
  1922.     int        always;
  1923. {
  1924.     win_redr_ruler(curwin, always);
  1925. }
  1926.  
  1927.     void
  1928. win_redr_ruler(wp, always)
  1929.     WIN        *wp;
  1930.     int        always;
  1931. {
  1932.     static linenr_t    oldlnum = 0;
  1933.     static colnr_t    oldcol = 0;
  1934.     char_u            buffer[30];
  1935.     int                row;
  1936.     int                fillchar;
  1937.  
  1938.     if (p_ru && (redraw_cmdline || always || wp->w_cursor.lnum != oldlnum || wp->w_virtcol != oldcol))
  1939.     {
  1940.         cursor_off();
  1941.         if (wp->w_status_height)
  1942.         {
  1943.             row = wp->w_winpos + wp->w_height;
  1944.             if (set_highlight('s') == OK)        /* can use highlighting */
  1945.             {
  1946.                 fillchar = ' ';
  1947.                 start_highlight();
  1948.             }
  1949.             else
  1950.                 fillchar = '=';
  1951.         }
  1952.         else
  1953.         {
  1954.             row = Rows - 1;
  1955.             fillchar = ' ';
  1956.         }
  1957.         /*
  1958.          * Some sprintfs return the lenght, some return a pointer.
  1959.          * To avoid portability problems we use strlen here.
  1960.          */
  1961.         sprintf((char *)buffer, "%ld,%d", wp->w_cursor.lnum, (int)wp->w_cursor.col + 1);
  1962.         if (wp->w_cursor.col != wp->w_virtcol)
  1963.             sprintf((char *)buffer + STRLEN(buffer), "-%d", wp->w_virtcol + 1);
  1964.  
  1965.         screen_start();            /* init cursor position */
  1966.         screen_msg(buffer, row, ru_col);
  1967.         screen_fill(row, row + 1, ru_col + (int)STRLEN(buffer), (int)Columns, fillchar, fillchar);
  1968.         oldlnum = wp->w_cursor.lnum;
  1969.         oldcol = wp->w_virtcol;
  1970.         stop_highlight();
  1971.     }
  1972. }
  1973.  
  1974. /*
  1975.  * screen_valid: Returns TRUE if there is a valid screen to write to.
  1976.  *                  Returns FALSE when starting up and screen not initialized yet.
  1977.  * Used by msg() to decide to use either screen_msg() or printf().
  1978.  */
  1979.     int
  1980. screen_valid()
  1981. {
  1982.     screenalloc(FALSE);        /* allocate screen buffers if size changed */
  1983.     return (Nextscreen != NULL);
  1984. }
  1985.